package com.jiangyg.mall.authz.support.authentication.member;

import com.jiangyg.mall.authz.constant.AuthzConstant;
import com.jiangyg.mall.authz.constant.SecurityConstant;
import com.jiangyg.mall.authz.support.authentication.AuthenticationRequestMatcher;
import com.jiangyg.mall.authz.support.authentication.exception.MemberAuthenticationException;
import com.jiangyg.mall.core.utils.Assert;
import com.jiangyg.mall.core.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 类描述：会员访问权限校验
 *
 * @author jiangyg
 * @date 2022-09-03
 */
@Slf4j
public class MemberAccessAuthenticationFilter extends GenericFilterBean {

    /**
     * 验证管理器
     */
    private final AuthenticationManager authenticationManager;

    /**
     * 请求地址匹配器，此过滤器只处理此请求
     */
    private final AuthenticationRequestMatcher matcher = new AuthenticationRequestMatcher();

    public MemberAccessAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        // 1. 请求类型判断
        if (!(servletRequest instanceof HttpServletRequest) || !(servletResponse instanceof HttpServletResponse)) {
            throw new ServletException("TokenAuthenticationFilter 仅支持 HTTP 请求！");
        }
        final HttpServletRequest request = (HttpServletRequest) servletRequest;
        final HttpServletResponse response = (HttpServletResponse) servletResponse;
        // 2. 判断是否为 authentication 请求，如果不是则退出
        if (!requiresAuthentication(request)) {
            chain.doFilter(request, response);
            return;
        }
        // 3. 获取待校验的请求地址
        final String authRequestPath = request.getParameter("path");
        if (StringUtils.isBlank(authRequestPath)) {
            throw new MemberAuthenticationException("没有匹配需要校验的path！");
        }
        // TODO 排除不需要登录的请求地址
        // 4. 通过会话获取认证信息
        Object memberInfo = request.getSession().getAttribute(AuthzConstant.MEMBER_INFO_SESSION_ID);
        if (memberInfo == null) {
            throw new MemberAuthenticationException("没有找到登录信息！");
        }
        Assert.notNull(memberInfo);
        final String memberInfoString = JsonUtils.toJSONString(memberInfo);
        request.setAttribute(SecurityConstant.AUTH_INFO_ATTR_NAME, Base64.encodeBase64String(memberInfoString.getBytes()));
        // 6. 继续执行过滤器
        chain.doFilter(request, response);
    }

    /**
     * 功能描述：判断请求是否满足匹配规则
     *
     * @param request 请求
     * @return 是否
     */
    private boolean requiresAuthentication(HttpServletRequest request) {
        return matcher.matches(request);
    }

}
